package com.example.sample.logic;

import android.annotation.TargetApi;
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Build;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.View;

import net.liteheaven.xblib.common.log.XLog;

import java.io.IOException;
import java.nio.ByteBuffer;

import static java.lang.Thread.sleep;

@TargetApi(16)
public class CodecWrapper {
    private static final String TAG = "MediaCodec";
    private static final String MIME_TYPE = "video/avc";    // H.264 Advanced Video Coding
    private static final long DEFAULT_TIMEOUT_US = 1000 * 10;
    private OnDecoderCallback onDecodeCallback;
    private SurfaceView surfaceView;
    private MediaCodec codec;
    private MediaFormat format;
    private String path;
    private MediaExtractor extractor;
    private Thread thread;
    private volatile boolean stop = false;

    // We avoid the device-specific limitations on width and height by using values that
    // are multiples of 16, which all tested devices seem to be able to handleEvt.
    public static int getVideoSize(int size) {
        int multiple = (int) Math.ceil(size/16.0);
        return multiple*16;
    }

    private MediaExtractor getExtractor(){
        if (extractor == null){
            try {
                extractor = new MediaExtractor();
                extractor.setDataSource(this.path);

                // 选择视频track
                for (int i = 0; i< extractor.getTrackCount(); i++){
                    MediaFormat format = extractor.getTrackFormat(i);
                    String mime = format.getString(MediaFormat.KEY_MIME);
                    if (mime.startsWith("video")) {
                        XLog.i(TAG, format.toString());
                        extractor.selectTrack(i);
                        this.format = format;
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return extractor;
    }

    private MediaFormat getMediaFormat(){
        getExtractor();
        return format;
    }

    public CodecWrapper(SurfaceView surfaceView, String path){
        this.surfaceView = surfaceView;
        this.path = path;
        try {
            //codec = MediaCodec.createDecoderByType(format.getString(MediaFormat.KEY_MIME));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void setOnDecodeCallback(OnDecoderCallback onDecodeCallback) {
        this.onDecodeCallback = onDecodeCallback;
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private boolean decode(){
        int inputIndex = codec.dequeueInputBuffer(DEFAULT_TIMEOUT_US);
        //XLog.d(TAG, "inputIndex: " + inputIndex);
        if (inputIndex >= 0) {
            ByteBuffer byteBuffer = codec.getInputBuffer(inputIndex);

            //读取一片或者一帧数据
            int sampleSize = extractor.readSampleData(byteBuffer,0);
            //Log.d(TAG, "sampleSize: " + sampleSize);
            if (sampleSize > 0) {
                codec.queueInputBuffer(inputIndex, 0, sampleSize, extractor.getSampleTime(), 0);
                //读取一帧后必须调用，提取下一帧
                boolean r = extractor.advance();
                //控制帧率在30帧左右
                try {
                    sleep(30);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{
                codec.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                return true;
            }
        }else{
            codec.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
            return true;
        }

        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        int outIndex = codec.dequeueOutputBuffer(bufferInfo, 10000);
        //XLog.d(TAG, "outIndex: " + outIndex);
        if (outIndex >= 0) {
            codec.releaseOutputBuffer(outIndex, true);
        }
        return false;
    }

    public void release() {
        if (null != codec) {
            codec.stop();
            codec.release();
            codec = null;
        }
    }

    private int index = 1;

    public void start(){
        if (thread == null) {
            surfaceView.setVisibility(View.VISIBLE);
            try {
                MediaFormat format = getMediaFormat();
                codec = MediaCodec.createDecoderByType(format.getString(MediaFormat.KEY_MIME));
                codec.configure(getMediaFormat(), surfaceView.getHolder().getSurface(), null, 0);
                codec.start();
            } catch (Exception e) {
                e.printStackTrace();
            }

            thread = new Thread(runnable);
            thread.setName("codec" + index++);
            stop = false;
            thread.start();
        }
    }

    public void stop(){
        if (thread != null){
            stop = true;
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            release();
            if (extractor != null){
                extractor.release();
                extractor = null;
            }
            thread = null;
        }

        surfaceView.setVisibility(View.INVISIBLE);
    }

    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            while (!stop){
                if (decode()){
                    break;
                }
                //XLog.i(TAG, "decode frame ~");
            }
            String name = Thread.currentThread().getName();
            XLog.i(TAG, "decode frame thread " + name + " exit~~");
        }
    };

    public interface OnDecoderCallback {
        void onFrame(byte[] yuvData);
    }
}
